Skip to content

System account #6559

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions kitsune/flagit/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ def flag(request, content_type=None, model=None, object_id=None, **kwargs):
content_type = get_object_or_404(ContentType, id=int(content_type))
content_object = get_object_or_404(content_type.model_class(), pk=object_id)

if (
content_type.model_class() == User
and hasattr(content_object, "profile")
and content_object.profile.is_system_account
):
return HttpResponseForbidden(_("System account content cannot be flagged."))

reason = request.POST.get("reason")
notes = request.POST.get("other", "")
next = request.POST.get("next")
Expand Down
6 changes: 3 additions & 3 deletions kitsune/forums/handlers.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType

from kitsune.flagit.models import FlaggedObject
from kitsune.forums.models import Post, Thread
from kitsune.users.handlers import UserDeletionListener
from kitsune.users.models import Profile


class ThreadListener(UserDeletionListener):
"""Handles thread cleanup when a user is deleted."""

def on_user_deletion(self, user: User) -> None:

sumo_bot = User.objects.get(username=settings.SUMO_BOT_USERNAME)
sumo_bot = Profile.get_sumo_bot()
Thread.objects.filter(creator=user).update(creator=sumo_bot)


class PostListener(UserDeletionListener):
"""Handles post cleanup when a user is deleted."""

def on_user_deletion(self, user: User) -> None:
sumo_bot = User.objects.get(username=settings.SUMO_BOT_USERNAME)
sumo_bot = Profile.get_sumo_bot()
posts = Post.objects.filter(author=user)

post_content_type = ContentType.objects.get_for_model(Post)
Expand Down
17 changes: 8 additions & 9 deletions kitsune/forums/tests/test_handlers.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType

from kitsune.flagit.models import FlaggedObject
from kitsune.forums.handlers import PostListener, ThreadListener
from kitsune.forums.models import Post
from kitsune.forums.tests import PostFactory, ThreadFactory
from kitsune.sumo.tests import TestCase
from kitsune.users.models import Profile
from kitsune.users.tests import UserFactory


class TestThreadListener(TestCase):
def setUp(self):
self.user = UserFactory()
User.objects.get_or_create(username=settings.SUMO_BOT_USERNAME)
self.sumo_bot = Profile.get_sumo_bot()
self.listener = ThreadListener()

def test_multiple_threads_reassignment(self):
Expand All @@ -26,7 +25,7 @@ def test_multiple_threads_reassignment(self):

for thread in [thread1, thread2, thread3]:
thread.refresh_from_db()
self.assertEqual(thread.creator.username, settings.SUMO_BOT_USERNAME)
self.assertEqual(thread.creator.username, self.sumo_bot.username)

def test_other_users_threads_unaffected(self):
"""Test that other users' threads are not affected."""
Expand All @@ -38,14 +37,14 @@ def test_other_users_threads_unaffected(self):

thread1.refresh_from_db()
thread2.refresh_from_db()
self.assertEqual(thread1.creator.username, settings.SUMO_BOT_USERNAME)
self.assertEqual(thread1.creator.username, self.sumo_bot.username)
self.assertEqual(thread2.creator, other_user)


class TestPostListener(TestCase):
def setUp(self):
self.user = UserFactory()
User.objects.get_or_create(username=settings.SUMO_BOT_USERNAME)
self.sumo_bot = Profile.get_sumo_bot()
self.listener = PostListener()

def test_multiple_posts_reassignment(self):
Expand All @@ -58,7 +57,7 @@ def test_multiple_posts_reassignment(self):

for post in [post1, post2, post3]:
post.refresh_from_db()
self.assertEqual(post.author.username, settings.SUMO_BOT_USERNAME)
self.assertEqual(post.author.username, self.sumo_bot.username)

def test_other_users_posts_unaffected(self):
"""Test that other users' posts are not affected."""
Expand All @@ -70,7 +69,7 @@ def test_other_users_posts_unaffected(self):

post1.refresh_from_db()
post2.refresh_from_db()
self.assertEqual(post1.author.username, settings.SUMO_BOT_USERNAME)
self.assertEqual(post1.author.username, self.sumo_bot.username)
self.assertEqual(post2.author, other_user)

def test_flagged_posts_deletion(self):
Expand Down Expand Up @@ -102,4 +101,4 @@ def test_flagged_posts_deletion(self):
post2.refresh_from_db()

post3.refresh_from_db()
self.assertEqual(post3.author.username, settings.SUMO_BOT_USERNAME)
self.assertEqual(post3.author.username, self.sumo_bot.username)
12 changes: 4 additions & 8 deletions kitsune/gallery/handlers.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from django.conf import settings
from django.contrib.auth.models import User

from kitsune.gallery.models import Image, Video
from kitsune.users.handlers import UserDeletionListener
from kitsune.users.models import Profile


class MediaListener(UserDeletionListener):
Expand All @@ -11,10 +11,6 @@ class MediaListener(UserDeletionListener):
def on_user_deletion(self, user: User) -> None:
"""Handle the deletion of a user."""

try:
sumo_bot = User.objects.get(username=settings.SUMO_BOT_USERNAME)
except User.DoesNotExist:
raise ValueError("SumoBot user not found")
else:
Image.objects.filter(creator=user).update(creator=sumo_bot)
Video.objects.filter(creator=user).update(creator=sumo_bot)
sumo_bot = Profile.get_sumo_bot()
Image.objects.filter(creator=user).update(creator=sumo_bot)
Video.objects.filter(creator=user).update(creator=sumo_bot)
6 changes: 3 additions & 3 deletions kitsune/kbforums/handlers.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
from django.conf import settings
from django.contrib.auth.models import User

from kitsune.kbforums.models import Post, Thread
from kitsune.users.handlers import UserDeletionListener
from kitsune.users.models import Profile


class ThreadListener(UserDeletionListener):
"""Handles thread cleanup when a user is deleted."""

def on_user_deletion(self, user: User) -> None:

sumo_bot = User.objects.get(username=settings.SUMO_BOT_USERNAME)
sumo_bot = Profile.get_sumo_bot()
Thread.objects.filter(creator=user).update(creator=sumo_bot)


Expand All @@ -19,5 +19,5 @@ class PostListener(UserDeletionListener):

def on_user_deletion(self, user: User) -> None:

sumo_bot = User.objects.get(username=settings.SUMO_BOT_USERNAME)
sumo_bot = Profile.get_sumo_bot()
Post.objects.filter(creator=user).update(creator=sumo_bot)
16 changes: 7 additions & 9 deletions kitsune/kbforums/tests/test_handlers.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
from django.conf import settings
from django.contrib.auth.models import User

from kitsune.kbforums.handlers import PostListener, ThreadListener
from kitsune.kbforums.tests import PostFactory, ThreadFactory
from kitsune.sumo.tests import TestCase
from kitsune.users.models import Profile
from kitsune.users.tests import UserFactory


class TestThreadListener(TestCase):
def setUp(self):
self.user = UserFactory()
User.objects.get_or_create(username=settings.SUMO_BOT_USERNAME)
self.sumo_bot = Profile.get_sumo_bot()
self.listener = ThreadListener()

def test_multiple_threads_reassignment(self):
Expand All @@ -23,7 +21,7 @@ def test_multiple_threads_reassignment(self):

for thread in [thread1, thread2, thread3]:
thread.refresh_from_db()
self.assertEqual(thread.creator.username, settings.SUMO_BOT_USERNAME)
self.assertEqual(thread.creator.username, self.sumo_bot.username)

def test_other_users_threads_unaffected(self):
"""Test that other users' threads are not affected."""
Expand All @@ -37,14 +35,14 @@ def test_other_users_threads_unaffected(self):

thread1.refresh_from_db()
thread2.refresh_from_db()
self.assertEqual(thread1.creator.username, settings.SUMO_BOT_USERNAME)
self.assertEqual(thread1.creator.username, self.sumo_bot.username)
self.assertEqual(thread2.creator, other_user)


class TestPostListener(TestCase):
def setUp(self):
self.user = UserFactory()
User.objects.get_or_create(username=settings.SUMO_BOT_USERNAME)
self.sumo_bot = Profile.get_sumo_bot()
self.listener = PostListener()

def test_multiple_posts_reassignment(self):
Expand All @@ -57,7 +55,7 @@ def test_multiple_posts_reassignment(self):

for post in [post1, post2, post3]:
post.refresh_from_db()
self.assertEqual(post.creator.username, settings.SUMO_BOT_USERNAME)
self.assertEqual(post.creator.username, self.sumo_bot.username)

def test_other_users_posts_unaffected(self):
"""Test that other users' posts are not affected."""
Expand All @@ -69,5 +67,5 @@ def test_other_users_posts_unaffected(self):

post1.refresh_from_db()
post2.refresh_from_db()
self.assertEqual(post1.creator.username, settings.SUMO_BOT_USERNAME)
self.assertEqual(post1.creator.username, self.sumo_bot.username)
self.assertEqual(post2.creator, other_user)
8 changes: 2 additions & 6 deletions kitsune/messages/handlers.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from django.conf import settings
from django.contrib.auth.models import User

from kitsune.messages.models import InboxMessage, OutboxMessage
from kitsune.users.handlers import UserDeletionListener
from kitsune.users.models import Profile


class MessageListener(UserDeletionListener):
Expand All @@ -14,11 +14,7 @@ def on_user_deletion(self, user: User) -> None:
- Delete their outbox messages
- Keep inbox messages for other users and reassign them to SumoBot
"""
try:
sumo_bot = User.objects.get(username=settings.SUMO_BOT_USERNAME)
except User.DoesNotExist:
raise ValueError("SumoBot user not found")

sumo_bot = Profile.get_sumo_bot()
InboxMessage.objects.filter(to=user).delete()
InboxMessage.objects.filter(sender=user).update(sender=sumo_bot)
OutboxMessage.objects.filter(sender=user).delete()
12 changes: 4 additions & 8 deletions kitsune/questions/handlers.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from dataclasses import dataclass, field

from django.conf import settings
from django.contrib.auth.models import User

from kitsune.questions.models import Answer, Question
from kitsune.sumo.handlers import AbstractChain, AccountHandler
from kitsune.users.handlers import UserDeletionListener
from kitsune.users.models import Profile


class SpamAAQHandler(AccountHandler):
Expand Down Expand Up @@ -78,13 +78,9 @@ def run(self, user: User) -> None:
handler.handle_account(data)

# Re-assign remaining questions and answers to SumoBot.
try:
sumo_bot = User.objects.get(username=settings.SUMO_BOT_USERNAME)
except User.DoesNotExist:
raise ValueError("SumoBot user not found")
else:
Question.objects.filter(creator=user).update(creator=sumo_bot)
Answer.objects.filter(creator=user).update(creator=sumo_bot)
sumo_bot = Profile.get_sumo_bot()
Question.objects.filter(creator=user).update(creator=sumo_bot)
Answer.objects.filter(creator=user).update(creator=sumo_bot)


class AAQListener(UserDeletionListener):
Expand Down
24 changes: 9 additions & 15 deletions kitsune/questions/tests/test_handlers.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
from django.conf import settings
from django.contrib.auth.models import User

from kitsune.products.tests import ProductFactory
from kitsune.questions.handlers import AAQChain
from kitsune.questions.models import Answer, Question
from kitsune.questions.tests import AnswerFactory, QuestionFactory
from kitsune.sumo.tests import TestCase
from kitsune.users.models import Profile
from kitsune.users.tests import UserFactory


class TestSpamAAQHandler(TestCase):
def setUp(self):
self.user = UserFactory()
User.objects.get_or_create(username=settings.SUMO_BOT_USERNAME)
self.sumo_bot = Profile.get_sumo_bot()
self.chain = AAQChain()

def test_spam_cleanup(self):
Expand All @@ -29,14 +29,14 @@ def test_spam_cleanup(self):

good_q.refresh_from_db()
good_a.refresh_from_db()
self.assertEqual(good_q.creator.username, settings.SUMO_BOT_USERNAME)
self.assertEqual(good_a.creator.username, settings.SUMO_BOT_USERNAME)
self.assertEqual(good_q.creator.username, self.sumo_bot.username)
self.assertEqual(good_a.creator.username, self.sumo_bot.username)


class TestArchivedProductAAQHandler(TestCase):
def setUp(self):
self.user = UserFactory()
User.objects.get_or_create(username=settings.SUMO_BOT_USERNAME)
self.sumo_bot = Profile.get_sumo_bot()
self.chain = AAQChain()

def test_archived_product_cleanup(self):
Expand All @@ -62,7 +62,7 @@ def test_archived_product_cleanup(self):
class TestOrphanedQuestionAAQHandler(TestCase):
def setUp(self):
self.user = UserFactory()
User.objects.get_or_create(username=settings.SUMO_BOT_USERNAME)
self.sumo_bot = Profile.get_sumo_bot()
self.chain = AAQChain()

def test_orphaned_question_cleanup(self):
Expand All @@ -83,22 +83,16 @@ class TestAAQChain(TestCase):
def setUp(self):
self.user = UserFactory()
self.chain = AAQChain()

def test_missing_sumo_bot(self):
"""Test that chain raises ValueError when SumoBot user doesn't exist."""

with self.assertRaises(ValueError):
self.chain.run(self.user)
self.sumo_bot = Profile.get_sumo_bot()

def test_reassign_to_sumo_bot(self):
"""Test that remaining content is reassigned to SumoBot."""
User.objects.get_or_create(username=settings.SUMO_BOT_USERNAME)
q = QuestionFactory(creator=self.user)
a = AnswerFactory(creator=self.user, question=q)

self.chain.run(self.user)

q.refresh_from_db()
a.refresh_from_db()
self.assertEqual(q.creator.username, settings.SUMO_BOT_USERNAME)
self.assertEqual(a.creator.username, settings.SUMO_BOT_USERNAME)
self.assertEqual(q.creator.username, self.sumo_bot.username)
self.assertEqual(a.creator.username, self.sumo_bot.username)
4 changes: 2 additions & 2 deletions kitsune/search/documents.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,8 +332,8 @@ class Index:
def prepare(cls, instance):
"""Override super method to exclude docs from indexing."""
# Add a discard field in the document if the following conditions are met
# User is not active
if not instance.user.is_active:
# User is not active or is a system account
if not instance.user.is_active or instance.is_system_account:
instance.es_discard_doc = "unindex_me"

return super(ProfileDocument, cls).prepare(instance)
Expand Down
15 changes: 6 additions & 9 deletions kitsune/search/signals/users.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
from django.db.models.signals import post_save, post_delete, m2m_changed
from django.contrib.auth.models import User, Group
from kitsune.users.models import Profile
from kitsune.search.es_utils import (
index_object,
delete_object,
remove_from_field,
)
from kitsune.search.decorators import search_receiver
from django.contrib.auth.models import Group, User
from django.db.models.signals import m2m_changed, post_delete, post_save

from kitsune.products.models import Product
from kitsune.search.decorators import search_receiver
from kitsune.search.es_utils import delete_object, index_object, remove_from_field
from kitsune.users.models import Profile


@search_receiver(post_save, User)
Expand Down
Loading